/*****************************************************
*
*               COPYRIGHT (c) 2017-2019 Hargic Corporation
*                         All Rights Reserved
*
* The source code contained or described herein and all documents
* related to the source code ("Material") are owned by Hargic
* Corporation or its licensors.  Title to the Material remains
* with Hargic Corporation or its suppliers and licensors.
*
* The Material is protected by worldwide copyright and trade secret
* laws and treaty provisions. No part of the Material may be used,
* copied, reproduced, modified, published, uploaded, posted, transmitted,
* distributed, or disclosed in any way except in accordance with the
* applicable license agreement.
*
* No license under any patent, copyright, trade secret or other
* intellectual property right is granted to or conferred upon you by
* disclosure or delivery of the Materials, either expressly, by
* implication, inducement, estoppel, except in accordance with the
* applicable license agreement.
*
* Unless otherwise agreed by Hargic in writing, you may not remove or
* alter this notice or any other notice embedded in Materials by Hargic
* or Hargic's suppliers or licensors in any way.
*
** Author: shengda.huang
** Date: 2019-03-20
*
*****************************************************/

/***** Include files ********************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <asm/current.h>
#include <asm/io.h>
#include <linux/gpio.h>

/***** Defines **************************************/
#define GPIO_PIN        4

#define LIGHT_MAJOR     0

enum
{
    LIGHT_OFF,
    LIGHT_ON,
};

/***** Define structure *****************************/
struct light_dev {
    struct cdev cdev;
    unsigned char value;
};

/***** Define constant ******************************/

/***** Define global ********************************/
struct light_dev *light_devp;
int light_major = LIGHT_MAJOR;

/***** Define Prototype of functions ****************/
MODULE_AUTHOR("ted huang <tednick@163.com>");
MODULE_LICENSE("GPL");

/**
 * @brief
 * @param  None
 * @retval None
 */
static void light_gpio_init(struct light_dev *dev)
{
    if(!gpio_is_valid(GPIO_PIN))
    {
        printk(KERN_ALERT "Error wrong gpio number\n");
        return ;
    }
    gpio_request(GPIO_PIN, "led_ctr");
    gpio_direction_output(GPIO_PIN, 1);
    gpio_set_value(GPIO_PIN, 1);
    dev->value = 1;
}

static void light_gpio_deinit(void)
{
    gpio_free(GPIO_PIN);
}

static void light_on(struct light_dev *dev)
{
    dev->value = 1;
    gpio_set_value(GPIO_PIN, 0);
}

static void light_off(struct light_dev *dev)
{
    dev->value = 0;
    gpio_set_value(GPIO_PIN, 1);
}

static int light_open(struct inode *inode, struct file *filp)
{
    struct light_dev *dev;

    dev = container_of(inode->i_cdev, struct light_dev, cdev);

    filp->private_data = dev;

    light_gpio_init(light_devp);

    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n", current->comm, current->pid, __FILE__, __func__, __LINE__);
    //get major and minor from inode
    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n", imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);
    return 0;
}

static int light_release(struct inode *inode, struct file *filp)
{
    light_gpio_deinit();
    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n", current->comm, current->pid, __FILE__, __func__, __LINE__);
    //get major and minor from inode
    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n", imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);
    return 0;
}

static ssize_t light_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    struct light_dev *dev = filp->private_data;

    if (copy_to_user(buf, &(dev->value), 1))
        return -EFAULT;

    struct inode *inode = filp->f_path.dentry->d_inode;
    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n", current->comm, current->pid, __FILE__, __func__, __LINE__);
    //get major and minor from inode
    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n", imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);
    return 0;
}

static ssize_t light_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
    struct light_dev *dev = filp->private_data;

    if (copy_from_user(&(dev->value), buf, 1))
        return -EFAULT;

    if (dev->value == 1)
        light_on(dev);
    else
        light_off(dev);

    struct inode *inode = filp->f_path.dentry->d_inode;
    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n", current->comm, current->pid, __FILE__, __func__, __LINE__);
    //get major and minor from inode
    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n", imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);
    return 1;
}

static long light_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct light_dev *dev = filp->private_data;

    switch (cmd)
    {
        case LIGHT_ON:
            light_on(dev);
            break;
        case LIGHT_OFF:
            light_off(dev);
            break;
        default:
            return -ENOTTY;
    }

    return 0;
}

static struct file_operations light_fops = {
    .owner = THIS_MODULE,
    .open = light_open,
    .release = light_release,
    .read = light_read,
    .write = light_write,
    .unlocked_ioctl = light_ioctl,
};


static void light_setup_cdev(struct light_dev *dev, int index)
{
    int err, devno = MKDEV(light_major, index);
    cdev_init(&dev->cdev, &light_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &light_fops;

    err = cdev_add(&dev->cdev, devno, 1);
    if (err)
        printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}

static int __init light_init(void)
{
    int result;

    dev_t dev = MKDEV(light_major, 0);

    if (light_major)
    {
        result = register_chrdev_region(dev, 1, "led_drv");
    }
    else
    {
        result = alloc_chrdev_region(&dev, 0, 1, "led_drv");
        light_major = MAJOR(dev);
    }
    if (result < 0)
        return result;

    light_devp = kmalloc(sizeof(struct light_dev), GFP_KERNEL);
    if (!light_devp)
    {
        result = -ENOMEM;
        goto err1;
    }

    memset(light_devp, 0, sizeof(struct light_dev));
    light_setup_cdev(light_devp, 0);

    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n", current->comm, current->pid, __FILE__, __func__, __LINE__);
    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - ok.\n", current->comm, current->pid, __FILE__, __func__, __LINE__);
    return 0;

err1:
    unregister_chrdev_region(dev, 0);
    return result;
}

static void __exit light_exit(void)
{
    cdev_del(&light_devp->cdev);
    kfree(light_devp);
    unregister_chrdev_region(MKDEV(light_major, 0), 1);

    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - leave.\n", current->comm, current->pid, __FILE__, __func__, __LINE__);
}

module_init(light_init);
module_exit(light_exit);

/* End of file */



